package com.master;

import java.io.File;
import java.io.FileOutputStream;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;

import com.utility.KJFFT;

public class FFT {

	private float[] left;
	private float[] right;
	private int sampleSize = 2048;
	private byte[] audioDataBuffer = null;
	private KJFFT fft = new KJFFT(512);
	private float[] old_FFT = new float[512];
	private int bande = 12;
	private int campioniLetti = 0;
	private int posizione = 0;
	private AudioFormat formato;
	private int offset = 0;
	private int dimensioneCampioni;
	private JsonFactory jsonFactory;

	public FFT() {
	}

	public void generaFFT(File inputFile, JsonFactory jsonFactory, int fps, int bande) {
		this.jsonFactory = jsonFactory;
		this.bande = bande;
		
		AudioInputStream din = null;
		AudioInputStream in = null;
		FileOutputStream fos = null;
		
		try {
			AudioFileFormat baseFileFormat = null;
			formato = null;
			baseFileFormat = AudioSystem.getAudioFileFormat(new File("1.mp3"));
			formato = baseFileFormat.getFormat();
			in = AudioSystem.getAudioInputStream(inputFile);
			
			AudioFormat decodedFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, formato.getSampleRate(), 16, formato.getChannels(), formato.getChannels() * 2, formato.getSampleRate(), false);
			
			din = AudioSystem.getAudioInputStream(decodedFormat, in);

			dimensioneCampioni = decodedFormat.getFrameSize();
			byte[] data = new byte[4096];
			
			audioDataBuffer = new byte[(int) formato.getSampleRate() << 1];

			int nBytesRead = 0;
			float passCount = 0;
			int sampleCount = 0;

			while (nBytesRead != -1) {
				
				nBytesRead = din.read(data, 0, data.length);
				
				if (nBytesRead != -1) {
					campioniLetti += (nBytesRead / decodedFormat.getFrameSize());
					storeAudioData(data, 0, nBytesRead);
					handleAudioBytes();
					passCount += 1000 / (formato.getSampleRate() / 1000);

					if (passCount >= (1000 / fps) * sampleCount) {
						//computeFFT(mergeStereo(left, right));
						computeFFT(left);
						sampleCount++;
					}

				}
			}
			jsonFactory.processFinished();
			//System.out.println("\nFinished!");
			//System.out.println("Processed " + (int) sampleCount / desiredFPS + "s of samples");
		} catch (Exception e) {
			
		} finally {
			try {
				if (din != null)
					din.close();
				if (in != null)
					in.close();
				if (fos != null) {
					fos.flush();
					fos.close();
				}
			} catch (Exception e1) {
			}

		}
	}

	private void storeAudioData(byte[] pAudioData, int pOffset, int pLength) {

		int wOverrun = 0;

		if (posizione + pLength > audioDataBuffer.length - 1) {
			wOverrun = (posizione + pLength) - audioDataBuffer.length;
			pLength = audioDataBuffer.length - posizione;
		}

		System.arraycopy(pAudioData, pOffset, audioDataBuffer, posizione,
				pLength);
		if (wOverrun > 0) {

			System.arraycopy(pAudioData, pOffset + pLength, audioDataBuffer, 0,
					wOverrun);
			posizione = wOverrun;

		} else {
			posizione += pLength;
		}

	}

	private void handleAudioBytes() {
		left = new float[sampleSize];
		right = new float[sampleSize];

		int c = (int) ((long) (campioniLetti * dimensioneCampioni) - (long) (audioDataBuffer.length * offset));

		if (c > 0) {
			for (int a = 0; a < sampleSize; a++, c += 4) {
				if (c >= audioDataBuffer.length) {
					c = (c - audioDataBuffer.length);
					offset++;
				}
				// channelMode == CHANNEL_MODE_STEREO && sampleType ==
				// SAMPLE_TYPE_SIXTEEN_BIT
				left[a] = ((audioDataBuffer[c + 1] << 8) + audioDataBuffer[c]) / 32767.0f;
				right[a] = ((audioDataBuffer[c + 3] << 8) + audioDataBuffer[c + 2]) / 32767.0f;
			}
		}
	}

	private float[] mergeStereo(float[] pLeft, float[] pRight) {

		for (int a = 0; a < pLeft.length; a++) {
			pLeft[a] = (pLeft[a] + pRight[a]) / 2.0f;
		}
		return pLeft;

	}

	private void computeFFT(float[] pSample) {
		
		int saMultiplier = (512 / 2) / bande;
		float c = 0;
		float[] wFFT = fft.calculate(pSample);
		float wSadfrr = (0.03f);
		float result[] = new float[bande];

		for (int a = 0, bd = 0; bd < bande; a += saMultiplier, bd++) {
			float wFs = 0;

			// -- Average out nearest bands.
			for (int b = 0; b < saMultiplier; b++) {
				wFs += wFFT[a + b];
			}

			// -- Log filter.
			wFs = (wFs * (float) Math.log(bd + 2));
			if (wFs > 1.0f) {
				wFs = 1.0f;
			}
			
			// -- Compute SA decay...
			if (wFs >= (old_FFT[a] /* - wSadfrr */)) {
				old_FFT[a] = wFs;
			} else {
				old_FFT[a] -= wSadfrr;
				if (old_FFT[a] < 0) {
					old_FFT[a] = 0;
				}
				wFs = old_FFT[a];
			}
			result[bd] = wFs;	
		}
		jsonFactory.handleFFT(result);
	}

}
